package com.hlkj.warboot.module_laboratory.configurer.aspect;

import com.alibaba.fastjson.JSONObject;
import com.hlkj.warboot.configurer.properties.RsaProperties;
import com.hlkj.warboot.configurer.util.LoggerUtil;
import com.hlkj.warboot.configurer.util.RSAUtil;
import com.hlkj.warboot.configurer.util.RequestUtil;
import com.hlkj.warboot.configurer.util.ResponseUtil;
import com.hlkj.warboot.module_sys.entity.LogEntity;
import com.hlkj.warboot.module_sys.service.LogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * @Description TODO 控制器参数AOP，入参解密，出错加密
 * @Author lixing
 * @Date 2020/6/16 21:11
 */
@Aspect
@Component
public class ControllerParamAspect {
    @Resource
    private RsaProperties rsaProperties;

    @Resource
    private LogService logService;

    /**
     * @Description TODO 定义一个切点pointCut。切入的位置: 哪些controller模块下的哪些方法
     * @Author lixing
     * @Date 2020/6/16 16:54
     */
    //    @Pointcut("execution(public * com.hlkj.warboot.*.controller.*.*(..))")
    @Pointcut("execution(public * com.hlkj.warboot.module_laboratory.controller.LaboratoryComtroller.aop1(..))||" +
            "execution(public * com.hlkj.warboot.module_laboratory.controller.LaboratoryComtroller.aop2(..))")
    public void pointCut() {
    }

    /**
     * @Description TODO 在方法调用之前和之后执行通知
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     * @Author lixing
     * @Date 2020/6/16 17:18
     */
    @Around("pointCut()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (servletRequestAttributes != null) {
            JSONObject reqParamForJSONObject = new JSONObject(); // TODO 存储请求的相关参数

            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
            if (httpServletRequest != null) {
                reqParamForJSONObject.put("reqIp", httpServletRequest.getRemoteAddr()); // 当前请求ip
                reqParamForJSONObject.put("reqUrl", RequestUtil.getRequestUrl(httpServletRequest)); // 当前请求url
                reqParamForJSONObject.put("reqType", httpServletRequest.getMethod()); // 请求类型 GET|POST
            }

            // HttpSession session = (HttpSession) servletRequestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);

            // 通知的签名
            Signature signature = proceedingJoinPoint.getSignature();
            reqParamForJSONObject.put("reqController", signature.getDeclaringTypeName()); // 代理的类
            reqParamForJSONObject.put("reqControllerMethod", proceedingJoinPoint.getSignature().getName()); // 代理的类方法

            // 对请求参数进行解密处理
            StringBuilder stringBuilderForreqParam = new StringBuilder();
            Object[] reqParamObjects = proceedingJoinPoint.getArgs();
            for (int i = 0; i < reqParamObjects.length; i++) {
                if (reqParamObjects[i] instanceof SecurityContextHolderAwareRequestWrapper) {
                    continue;
                }
                if (reqParamObjects[i] instanceof String) {
                    try {
                        reqParamObjects[i] = RSAUtil.decryptByPrivateKey(rsaProperties.getPrivateKey(), reqParamObjects[i].toString() ); // 解密处理
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    stringBuilderForreqParam.append(reqParamObjects[i]);
                } else {
                    stringBuilderForreqParam.append(JSONObject.toJSONString(reqParamObjects[i]));
                }
                stringBuilderForreqParam.append(";");
            }
            try {
                proceedingJoinPoint.proceed(reqParamObjects); // TODO **** 使修改后的参数生效 ****
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            } finally {
                reqParamForJSONObject.put("reqParam", stringBuilderForreqParam);
            }

            try {
                Object responseParam = proceedingJoinPoint.proceed();
                // TODO ====================================================================以上代码会在执行相关方法体之前执行
                // TODO ====================================================================以下代码会在执行完相关方法体之后执行
                // 对响应的参数进行加密
                JSONObject jsonObjectParam = (JSONObject) responseParam;
                if (jsonObjectParam.getInteger("code") == 0) {
                    Object jsonObjectParam_data = jsonObjectParam.get("data");
                    if (jsonObjectParam_data != null) {
                        jsonObjectParam.put("data", RSAUtil.encryptByPrivateKey(rsaProperties.getPrivateKey(), JSONObject.toJSONString(jsonObjectParam_data)));
                    }
                }
                reqParamForJSONObject.put("responseParam", jsonObjectParam.toJSONString());

                // TODO 入参和出参存入数据库
                try {
                    LogEntity logEntity = new LogEntity();
                    logEntity.setCategory(2); // 行为日志
                    logEntity.setCreateUserAccount("ControllerParamAspect"); // 行为日志的创建账户为其AOP的类名
                    logEntity.setCreateUserGrade(1); // 行为日志的创建等级为1，表示只有超级管理员才能看到
                    logEntity.setContent(reqParamForJSONObject.toString());
                    logService.save(logEntity);
                } catch (Exception e) {
                    LoggerUtil.error("ControllerParamAspect参数存入数据库出错：" + e.getMessage());
                }
                return jsonObjectParam;
            } catch (Throwable e) {
                return ResponseUtil.fail(e.getMessage());
            }
        }
        return ResponseUtil.fail("ServletRequestAttributes获取失败");
    }

    /**
     * @Description TODO 前置通知。在某连接点之前执行的通知，但这个通知不能阻止连接点之前的执行流程（除非它抛出一个异常）
     * @Author lixing
     * @Date 2020/6/16 16:54
     */
//    @Before("pointCut()")
//    public Object doBeforeAdvice(ProceedingJoinPoint proceedingJoinPoint) {
//        Object[] reqParamObjects = proceedingJoinPoint.getArgs();
//        String param = null;
//        for (Object reqParamObj : reqParamObjects) {
//            if (reqParamObj instanceof SecurityContextHolderAwareRequestWrapper) {
//                continue;
//            }
//            if (reqParamObj instanceof String) {
//
//            } else {
//
//            }
//
//        }
//
//        try {
//            Object obj = proceedingJoinPoint.proceed(reqParamObjects);
//            return proceedingJoinPoint.proceed();
//        } catch (Throwable throwable) {
//            throwable.printStackTrace();
//            return null;
//        }
//    }

    /**
     * @Description TODO 方法执行成功之后执行通知
     *  如果参数中的第一个参数为JoinPoint，则第二个参数为返回值的信息
     *  如果参数中的第一个参数不为JoinPoint，则第一个参数为returning中对应的参数
     * @Author lixing
     * @Date 2020/6/16 16:56
     */
//    @AfterReturning(value = "pointCut()", returning = "returnParams")
//    public void doAfterReturningAdvice(JoinPoint joinPoint, Object returnParams) {
//        System.out.println("修改完毕-->返回方法为:" + returnParams);
//    }

}
